/*
 * MPlayer.cpp
 *
 *  Created on: 2018年9月4日
 *      Author: zhengboyuan
 */

#include "MPlayer.h"
#include "TStringUtil.h"
#include "MediaFormatJson.h"
#include "MFormatUtil.h"
#include "TFileUtil.h"


MPlayer::MPlayer():
	m_handle(),
	m_canPlay(),
	m_file()
{
}

MPlayer::~MPlayer()
{
	close();
}

bool MPlayer::open(const std::string& url)
{
#ifdef WIN32
	m_videoWindow.open("Video");
#endif //WIN32

	m_file = fopen(url.c_str(), "rb");
	if (!m_file)
	{
		return false;
	}

	av::MediaFormat format;
	if (!readFormat(format))
	{
		return false;
	}

	if (format.getVideoPropSize() > 0)
	{
		//comn::FileUtil::write(format.getVideoProp(), format.getVideoPropSize(), "in.h264", false);
	}
	av_usleep(10000);

	m_handle = MDecode_CreateHandle();

	MDecode_Format fmt = MDecode_Format();
	fmt.codec = (MDecode_CodecType)format.m_codec;
	fmt.prop = (uint8_t*)(format.getVideoProp());
	fmt.propSize = format.getVideoPropSize();

	fmt.channels = format.m_channels;
	fmt.samplerate = format.m_sampleRate; 
	//fmt.audioCodec = (MDecode_CodecType)format.m_audioCodec;

	BOOL ret = MDecode_OpenStream(m_handle, &fmt);

	//MDecode_SetDecodeCallback(handle, MDECODE_PIXEL_I420, Application::decodeCallback, &app);
	//MDecode_SetDecodeCallbackFreq(handle, 10);
	MDecode_SetDelayTime(m_handle, 1000);
	MDecode_SetFaceTrackDuration(400);

	MDecode_SetParam(m_handle, "video.show_frame", "1");


#ifdef WIN32
	MDecode_SetVideoWnd(m_handle, m_videoWindow.getHwnd());
#endif //WIN32

	MDecode_Play(m_handle);

	return start();
}

void MPlayer::close()
{
	if (isRunning())
	{
		stop();
	}

	MDecode_Stop(m_handle);
	MDecode_CloseStream(m_handle);
	MDecode_DestroyHandle(m_handle);

	m_pktQueue.clear();

	
#ifdef WIN32
	m_videoWindow.close();
#endif //WIN32
}

HANDLE MPlayer::getHandle()
{
	return m_handle;
}

void MPlayer::inputSubtitle(const std::string& text, int64_t pts)
{
	MDecode_InputData(m_handle, MDECODE_MEDIA_SUBTITLE, (const uint8_t*)text.c_str(), text.size(), pts, 0);
}

int MPlayer::run()
{
	while (!m_canExit)
	{
		AVPacketPtr pkt(new AVPacket(), AVPacketDeleter());
		av_init_packet(pkt.get());
		if (!readPacket(pkt))
		{
			break;
		}

		onInputPacket(pkt);

		m_pktQueue.push(pkt);

		if (m_canPlay)
		{
			pkt = m_pktQueue.pop();
			if (pkt)
			{
				MDecode_InputData(m_handle, pkt->stream_index, pkt->data, pkt->size, pkt->pts, pkt->flags);
			}
		}
	}
	return 0;
}

void MPlayer::doStop()
{
	m_event.post();
}

void MPlayer::onInputPacket(AVPacketPtr& pkt)
{
	comn::TimePoint timePoint(av_gettime_relative(), pkt->pos);

	if (!m_timePoint.isSet())
	{
		m_timePoint = timePoint;
	}

	int64_t offset = timePoint.getOffset(m_timePoint);
	if (offset > 1000 * 160)
	{
		timePoint.reset();
	}
	else if (offset > 1000 * 20)
	{
		av_usleep(offset);
	}

	if (pkt->stream_index == 0)
	{
		//comn::FileUtil::write(pkt->data, pkt->size, "in.h264", true);
	}
}

void MPlayer::play()
{
	m_canPlay = true;
}

void MPlayer::pause()
{
	m_canPlay = false;
}

void MPlayer::onFace(const std::string& text, int64_t pts)
{
	MDecode_InputData(m_handle, MDECODE_MEDIA_SUBTITLE, (const uint8_t*)text.c_str(), text.size(), pts, 0);
}

bool MPlayer::readFormat(av::MediaFormat& format)
{
	av::PacketHeader header = av::PacketHeader();
	int size = fread(&header, 1, sizeof(header), m_file);
	if (size != sizeof(header))
	{
		return false;
	}

	ByteBuffer buffer;
	buffer.ensure(header.size);

	size = fread(buffer.data(), 1, header.size, m_file);
	if (size != header.size)
	{
		return false;
	}

	Json::Value value;
	Json::Reader reader;
	if (!reader.parse((char*)buffer.data(), (char*)buffer.data() + header.size, value))
	{
		return false;
	}

	return av::MediaFormatJson::fromJson(format, value);
}

bool MPlayer::readPacket(AVPacketPtr& pkt)
{
	av::PacketHeader header = av::PacketHeader();
	int size = fread(&header, 1, sizeof(header), m_file);
	if (size != sizeof(header))
	{
		return false;
	}

	av_new_packet(pkt.get(), header.size);
	pkt->size = fread(pkt->data, 1, header.size, m_file);

	if (pkt->size <= 0)
	{
		return false;
	}

	pkt->pts = header.ts;
	pkt->flags = header.flags;
	pkt->duration = header.duration;
	pkt->stream_index = header.type;
	
	pkt->pos = header.clock;

	return true;
}